package com.starsoft.quant.executor;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.starsoft.frame.util.DateUtil;
import com.starsoft.frame.util.EmailService;
import com.starsoft.quant.bean.QuantSummary;
import com.starsoft.quant.bean.QuantSummaryId;
import com.starsoft.quant.service.QuantAcountService;
import com.starsoft.quant.service.QuantReportService;
import com.starsoft.smdc.bean.SmdcDiv;
import com.starsoft.smdc.bean.SmdcSecurity;
import com.starsoft.smdc.service.DivService;
import com.starsoft.smdc.service.HolidayService;
import com.starsoft.smdc.service.SecurityService;
import com.starsoft.smdc.util.SecurityType;
import com.starsoft.trade.bean.TradeAcountStatus;
import com.starsoft.trade.bean.TradeBill;
import com.starsoft.trade.bean.TradePosition;
import com.starsoft.trade.order.Order;
import com.starsoft.trade.service.AcountStatusService;
import com.starsoft.trade.service.TradeBillService;
import com.starsoft.trade.service.TradeFeeService;
import com.starsoft.trade.trader.LocalTrader;
import com.starsoft.trade.trader.TraderService;
import com.starsoft.trade.util.TradeType;

@Service
public class ExecutorService {

	private static Logger logger = LoggerFactory.getLogger(ExecutorService.class);
	
	@Autowired
	EmailService emailService;
	
	@Autowired
	TradeBillService billService;
	
	@Autowired
	TradeFeeService feeService;
	
	@Autowired
	SecurityService securityService;
	
	@Autowired
	AcountStatusService acountStatusService;
	
	@Autowired
	QuantReportService quantReportService; 
	
	@Autowired
	DivService divService;

	@Autowired
	HolidayService holidayService;
	
	@Autowired
	QuantReportService reportService;
	
	@Autowired
	QuantAcountService quantAcountService;
	
	@Autowired
	LocalTrader localTrader;
	
	@Autowired
	TraderService traderService;
		
	public void deleteBeforeBackTest(Integer acountId, Date startDate){
		billService.deleteBillsAfter(acountId, startDate);
		acountStatusService.deleteStatusAfter(acountId, startDate);
		quantReportService.deleteReportBetween(acountId, startDate, null);
	}
	
	public void doDiv(ExecutorContext context){
		Integer acountId = context.getModelContext().getAcountId();
		TradeAcountStatus status = context.getAcountStatus();
		List<TradePosition> positions =  status.getPositions();
		List<TradeBill> bills = new ArrayList<>();
		for(TradePosition position : positions){
			SmdcDiv div = divService.get(position.getId().getSecId(), context.getCurrentDate());
			if(div != null){
				TradeBill bill = localTrader.trade(acountId, position, div);
				if(bill!=null){
					bills.add(bill);
				}
			}
		}
		if(!bills.isEmpty()){
			status =acountStatusService.calculateStatus(context.getCurrentDate(), context.getAcountStatus(), bills);
			context.setAcountStatus(status);
		}
	}
	
	public double getOrderMoney(Integer acountId, Order order){
		TradeBill bill = new TradeBill();
		bill.setAcountId(acountId);
		bill.setSecId(order.getSecId());
		bill.setSecNum(order.getSecNum());
		bill.setSecPrice(order.getPrice());
		bill.setBillDesc(order.getDesp());
		SmdcSecurity security = securityService.get(order.getSecId());
		SecurityType securityType = SecurityType.get(security);
		TradeType tradeType = null;
		if(securityType == SecurityType.stock){
			if(order.getSecNum()>0) tradeType = TradeType.stockbuy;
			else tradeType = TradeType.stocksell;
		} else if(securityType == SecurityType.fund){
			if(order.getSecNum()>0) tradeType = TradeType.fundbuy;
			else tradeType = TradeType.fundsell;
		} else if(securityType == SecurityType.index){
			if(order.getSecNum()>0) tradeType = TradeType.indexbuy;
			else tradeType = TradeType.indexsell;
		}
		bill.setBillType(tradeType.name());
		billService.dealMoney(bill);
		return bill.getBillMoney();
	}
	
	public void saveReport(ExecutorContext context, StrategyReport report) {
		Integer acountId = context.getModelContext().getAcountId();
		boolean hasOrders = report.getOrders() != null && report.getOrders().size()>0;
		boolean sendEmail = Boolean.TRUE.equals(context.getModelContext().getQuantAcount().getSendEmail());
		boolean isTradeAuto = context.getModelContext().getTradeAuto();
		boolean canBackTest = quantAcountService.canBackTest(acountId);

		if(hasOrders && sendEmail){
			report.getSummary().setSendEmail(true);
		} else {
			report.getSummary().setSendEmail(false);
		}
		
		if (hasOrders && context.isTradeTime() && sendEmail && report.getOrders().size()>1) {
			sendEmail(context, report);
		}
		if (hasOrders && context.isBackTest() && canBackTest) {
			doOrders(context, report);
		}
		if (hasOrders && context.isTradeTime() && isTradeAuto) {
			doOrders(context, report);
		}
		saveToDB(context, report);
	}
	
	private void sendEmail(ExecutorContext context, StrategyReport report){
		QuantSummary lastSummary = reportService.getSummary(context.getModelContext().getAcountId(), context.getCurrentDate());
		if(lastSummary != null && lastSummary.getSendEmail()!=null && lastSummary.getSendEmail()){
			return;
		}

		String emailTo = context.getModelContext().getUser().getEmail();
		String acountName = context.getModelContext().getAcount().getAcountName();		
		String subject = acountName+",order size:" + report.getOrders().size();		
		
		StringBuilder content = new StringBuilder();
		content.append("<table border='1'>");
		content.append("<tr>");
		content.append("<td>").append("代码").append("</td>");
		content.append("<td>").append("名称").append("</td>");
		content.append("<td>").append("持仓").append("</td>");
		content.append("<td>").append("评价").append("</td>");
		content.append("<td>").append("交易").append("</td>");
		content.append("<td>").append("价格").append("</td>");
		content.append("</tr>");
		
		for(Order order : report.getOrders()){
			String secId = order.getSecId();
			TradePosition pos = context.getAcountStatus().getPosition(secId);
			Double posNum = 0d;
			if(pos != null){
				posNum = pos.getSecNum();
			}
			content.append("<tr>");
			content.append("<td>").append(secId).append("</td>");
			content.append("<td>").append(securityService.get(secId).getSecName()).append("</td>");
			content.append("<td>").append(posNum).append("</td>");
			content.append("<td>").append(report.getDetail(secId).getRating()).append("</td>");
			content.append("<td>").append(order.getSecNum()).append("</td>");
			content.append("<td>").append(order.getPrice()).append("</td>");
			content.append("</tr>");
		}
		content.append("</table>");

		emailService.sendEmail(emailTo, subject, content.toString());
	}
	
	private void saveToDB(ExecutorContext context, StrategyReport report){
		acountStatusService.saveStatus(context.getAcountStatus());
		reportService.saveDetails(report.getDetails());
		reportService.saveSummary(report.getSummary());
	}
	
	private void doOrders(ExecutorContext context, StrategyReport report) {
		Integer acountId = context.getModelContext().getAcountId();
		boolean hasExSysem = quantAcountService.hasExSystem(acountId);
		if (hasExSysem && context.isTradeTime()) {
			logger.info(DateUtil.toString(context.getCurrentDate()) + " Ex System Auto Trade " + report.getOrders().size());
			TradeAcountStatus status = context.getAcountStatus();
			traderService.trade(status.getTradeAcount(), report.getOrders(), status);
			logger.info(DateUtil.toString(context.getCurrentDate()) + " Local Auto Trade " + report.getOrders().size());
			List<TradeBill> bills = traderService.synchBill(context.getModelContext().getTradeAcount(), context.getCurrentDate());
			status = acountStatusService.calculateStatus(context.getCurrentDate(), status, bills);
			context.setAcountStatus(status);
		} else {
			logger.info(DateUtil.toString(context.getCurrentDate()) + " Local Auto Trade " + report.getOrders().size());
			List<TradeBill> bills = localTrader.trade(acountId, context.getCurrentDate(), report.getOrders());
			TradeAcountStatus status = context.getAcountStatus();
			status = acountStatusService.calculateStatus(context.getCurrentDate(), status, bills);
			context.setAcountStatus(status);
		}
	}
	
	public StrategyReport saveException(ExecutorContext context, Exception e){
		Integer acountId = context.getModelContext().getAcountId();
		QuantSummaryId summaryId = new QuantSummaryId(acountId, context.getCurrentDate());
		QuantSummary summary = new QuantSummary(summaryId);
		summary.setRunTime(new Timestamp(new Date().getTime()));
		
		StringWriter runLongSw= new StringWriter();
		PrintWriter runLongPw = new PrintWriter(runLongSw);
		e.printStackTrace(runLongPw);
		summary.setRunLog(runLongSw.toString());
		reportService.saveSummary(summary);
		
		StrategyReport strategyReport = new StrategyReport();
		strategyReport.setSummary(summary);
		return strategyReport;
	}
	
}
